package com.foohoo.benchmark.collection;

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 测试不同的ArrayList集合遍历方式的性能
 * Benchmark                                   Mode  Cnt   Score    Error  Units
 * ArrayListLoop.loopWithFor                   avgt    3  17.984 ± 45.694  ns/op
 * ArrayListLoop.loopWithForeach               avgt    3  15.028 ±  8.589  ns/op
 * ArrayListLoop.loopWithForeachWrapByForeach  avgt    3  20.779 ± 14.738  ns/op
 * ArrayListLoop.loopWithIterator              avgt    3  15.085 ±  7.816  ns/op
 * ArrayListLoop.loopWithStream                avgt    3  69.652 ± 47.572  ns/op
 * 一句话结论: foreach(for in)最快， iterator次之, 然后是for, 但是jit优化后差距很小; foreach关键字是对for in的包装，增加了额外的损耗;stream最慢
 *
 * @author mzdbxqh
 * @date 2020-10-20 09:28
 **/
@Slf4j
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(1)
@State(Scope.Benchmark)
@Threads(1)
public class ArrayListLoop {

	private static List<String> list;

	private final static int COLLECTION_SIZE = 8192;

	private final static String COLLECTION_FILL = "EMPTY";

	@Setup
	public void initialize() {
		list = new ArrayList<>(COLLECTION_SIZE);
		Collections.fill(list, COLLECTION_FILL);
	}

	@Benchmark
	public void loopWithIterator(Blackhole bh) {
		Iterator<String> it = list.iterator();
		StringBuffer buffer = new StringBuffer();
		while (it.hasNext()) {
			String str = it.next();
			buffer.append(str);
		}
		String result = buffer.toString();
		bh.consume(result);
	}

	@Benchmark
	public void loopWithForeach(Blackhole bh) {
		StringBuffer buffer = new StringBuffer();
		for (String str : list) {
			buffer.append(str);
		}
		String result = buffer.toString();
		bh.consume(result);
	}

	@Benchmark
	public void loopWithForeachWrapByForeach(Blackhole bh) {
		StringBuffer buffer = new StringBuffer();
		list.forEach(buffer::append);
		String result = buffer.toString();
		bh.consume(result);
	}

	@Benchmark
	public void loopWithStream(Blackhole bh) {
		StringBuffer buffer = new StringBuffer();
		list.stream().map(buffer::append);
		String result = buffer.toString();
		bh.consume(result);
	}

	@Benchmark
	public void loopWithFor(final Blackhole bh) {
		StringBuffer buffer = new StringBuffer();
		int size = list.size();
		for (int i = 0; i < size; i++) {
			buffer.append(list.get(i));
		}
		String result = buffer.toString();
		bh.consume(result);
	}
}
